package edu.pku.sei.transformation.serialization;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceFactoryImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;

import edu.pku.sei.bridge.mdabase.adapter.ECoreLoader;
import edu.pku.sei.mdabase.basictypes.OCLVoidValue;
import edu.pku.sei.mdabase.infrastructure.metamodel.MetaReference;
import edu.pku.sei.mdabase.infrastructure.model.MAttribute;
import edu.pku.sei.mdabase.infrastructure.model.MClass;
import edu.pku.sei.mdabase.infrastructure.model.MModelGraph;
import edu.pku.sei.mdabase.infrastructure.model.MReference;

public class EMFXMISaver {

	private ECoreLoader ecoreLoader = null;
	
	private HashMap<MClass, EObject> MClassToEObj = new HashMap<MClass, EObject>();
	
	private List<EObject> savedObject = new ArrayList<EObject>();

	public void save(MModelGraph model, String modelFileName) {
		ecoreLoader = ECoreLoader.getECoreLoader(model.getTypeGraph().getName());
		if(ecoreLoader == null)
			return;
		ResourceSet resourceSet = new ResourceSetImpl();
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new EcoreResourceFactoryImpl());
		URI fileURI = URI.createFileURI(modelFileName);
		File file = new File(modelFileName);
		XMLResource resource = null;
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(  

				   Resource.Factory.Registry.DEFAULT_EXTENSION,  
				   new ResourceFactoryImpl()  
				   {  
					   public Resource createResource(URI uri)  

					   {  
						   return new XMIResourceImpl(uri)  
						   {
							   protected boolean useUUIDs() { return false; }  
						   };  

					   }  
				  });
		resource = (XMLResource) resourceSet.createResource(fileURI);
		MT2EMF(model, ecoreLoader, resource);
		//Resource resource = getResource(modelFileName);
		try {
			Map<String, Boolean> options = new HashMap<String, Boolean>();
			options.put(XMLResource.OPTION_SAVE_TYPE_INFORMATION, Boolean.TRUE);
			options.put(XMLResource.OPTION_KEEP_DEFAULT_CONTENT, Boolean.TRUE);
			resource.save(options);
		} catch (IOException e) {
		}
	}

	public void MT2EMF(MModelGraph model, ECoreLoader ecoreLoader, Resource resource) {
		EPackage pack = ecoreLoader.getRootPackage();
		EFactory factory = pack.getEFactoryInstance();
		for(MClass mCls : model.getNodes()){
			EClass eCls = (EClass) pack.getEClassifier(mCls.getType().getName());
			EObject eObj = factory.create(eCls);
			savedObject.add(eObj);
			MClassToEObj.put(mCls, eObj);
			mapClass(mCls, eObj);
		}
		for(MReference mRef : model.getEdges()){
			mapReference(mRef);
		}
		List<EObject> newList = new ArrayList<EObject>();
		for (EObject eobj : savedObject)
		{
			if (eobj.eContainer() == null)
				newList.add(eobj);
		}
		resource.getContents().addAll(newList);
	}

	private void mapClass(MClass mCls, EObject eObj){
		EClass eCls = eObj.eClass();
		//NOTE: useless when you dont need an ID
//		String id = mCls.getID();
//		if (id == null)
//		{
//			id = EcoreUtil.generateUUID();
//			mCls.setID(id);
//		}
		for(MAttribute mAttr : mCls.getAttributes()){
			Object value = mAttr.getValue();
			if (value instanceof OCLVoidValue)
				value = null;
			eObj.eSet(eCls.getEStructuralFeature(mAttr.getName()), value);
		}
		//EcoreUtil.setID(eObj, mCls.getID());
	}
	
	@SuppressWarnings("unchecked")
	private void mapReference(MReference mRef){
		MClass msource = mRef.getSource();
		MClass mtarget = mRef.getTarget();
		EObject esource = MClassToEObj.get(msource);
		EObject etarget = MClassToEObj.get(mtarget);
		MetaReference type = (MetaReference) mRef.getType();
		EReference eref = (EReference) esource.eClass().getEStructuralFeature(type.getName());
		if(eref.getUpperBound()!=1){
			((EList<EObject>)esource.eGet(eref)).add(etarget);
		}else{
			esource.eSet(eref, etarget);
		}
	}
}